CloudFrontでBrotli圧縮したコンテンツを配信してみた
はじめに
BrotliはGoogleが開発した圧縮フォーマットです。
RFC 7932で仕様公開されており、gzipやZopfliよりもパフォーマンスがよく、Chrome、Safari、Firefox、Edgeといったすべての主要ブラウザが対応しています。Content-Encoding
は br
です。
より効率的にエンコードすることで、より速く、より安くコンテンツ配信できます。
今回はこのBrotliによるコンテンツ圧縮をCloudFrontと連携される方法を3種類ご紹介します。
- (未対応)CloudFrontで動的にBrotli圧縮
- オリジンで動的にBrotli圧縮
- オリジンで静的にBrotli圧縮
(未対応)1. CloudFrontで動的にBrotli圧縮
CloudFront Behaviorには「Compress Objects Automatically」という設定が存在し、クライアントのAccept-Encoding
に応じて適切なエンコーディング形式でコンテンツを返す機能が存在します。
残念ながら、現時点ではgzipエンコーディングにしか対応していないため、この方式ではBrotli配信できません。
CloudFrontの公式フォーラムでロードマップの意見を募集しているので、Brotli対応にプラス投票をすれば、思いが伝わるかもしれません。
AWS Developer Forums: Amazon CloudFront wants to hear your input on the service roadmap
2020年9月に Brotli 対応しました!
2. オリジンで動的にBrotli圧縮
オリジンで動的(オンデマンド)にBrotli圧縮します。
ポイントは、CloudFrontでAccept-Encoding
ヘッダーをホワイトリスト化することです。
クライアントのAccept-Encoding
をそのままオリジンにフォワードしつつ、Accept-Encoding
に基づいてコンテンツをキャッシュできるようになります。
ホワイトリスト化しなかった場合、次のようにAccept-Encoding
からgzip以外の情報が失われてしまいます。
Accept-Encoding
にgzipが含まれていれば、Accept-Encoding: gzip
に書き換える。gzip以外は削除Accept-Encoding
にgzipが含まれていなければ、ヘッダーフィールドそのものを削除
Nginxで動的にBrotli圧縮
Nginxの場合、Googleがモジュールngx_brotliを提供しています。
同モジュールを有効後、
brotli on; # enable on-the-fly compression brotli_comp_level 6; # compression level : 0 - 11 brotli_types text/plain ... application/atom+xml applic # MIME types in addition to text/html
のようなディレクティブを追加すると、オンデマンドでBrotli圧縮します。
3. オリジンで静的にBrotli圧縮
オリジンでファイルを事前にBrotli圧縮しておき、Accept-Encoding
ヘッダーに応じてBrotliエンコーディングされたファイルを返します。
当然ながら、事前にBrotli圧縮する手間が発生します。
Nginxで静的配信
CloudFrontでAccept-Encoding
ヘッダーをホワイトリスト化するのは動的配信と同じです。
Nginxの場合、Googleがモジュールngx_brotliを提供しています。
同モジュールを有効後、
brotli_static on; # enables checking of the existence of pre-compressed files with.br extension
のようなディレクティブを追加します。
Brotliエンコーディングされた.br
ファイルが存在する場合、同ファイルを返します。
S3で静的配信
オリジンがS3のケースです。
事前にファイルをBrotli圧縮し、
- Content-Type : コンテンツごとに異なる
- Content-Encoding : br
を適切に設定したS3オブジェクトをPUTします。
CloudFrontでAccept-Encoding
ヘッダーフィールドをホワイトリスト化するのは動的配信と同じです。
クライアントがBrotliエンコーディングに対応する場合、Lambda@EdgeのOrigin RequestでURIをBrotliファイル向けに書き換えます。
def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] encoding = headers.get('accept-encoding', [{}])[0] if 'br' in encoding.get('value', ''): # クライアントが Brotli に対応している request['uri'] = request['uri'] + '.br' # URI を Brotli ファイル向けに書き換える return request
Brotliに対応していないクライアント向けには、CloudFrontのBehaviorで「Compress Objects Automatically」を有効にしておけば、gzip圧縮されます。 オリジンが Content-Encoding を含んだレスポンスを返す場合、CloudFront がさらにエンコードすることは有りません。
手間がかかるため、特殊な条件が揃わない限りこのような構成は積極的には採用しないと思いますが、このような構成も可能です。
最後に
CloudFrontからBrotliエンコーディングで配信する場合、現時点ではAccept-Encoding
ヘッダーフィールドをホワイトリスト化し、オリジンでBrotli圧縮する必要があります。
CloudFrontのAccept-Encoding
をホワイトリスト化しなかった場合、オリジンへのリクエスト時にgzip 以外の対応エンコーディングを削除します。Brotliに限らず、クライアントのAccept-Encoding
に応じてオリジンでエンコードしている場合は、ご注意ください。